#ifndef GAMELOGIC_H
#define GAMELOGIC_H

#include <iostream>

/*

Issues:

It would be good if we could remove the explicit templating of the maths and comparison nodes.
For this we would require polymorphic operators (I think) and variables/attributes must derive
from the one base class (maybe boost::any?).

Is it possible to template operators? Could save us some trouble.

*/

//////////////////////// Abstract Nodes ////////////////////////

class ControlNode
{
public:
    virtual void activate() = 0;
};

class HeadNode : public ControlNode
{
public:
    ControlNode* topnode;

};

class LogicNode
{
public:
    virtual bool evaluate() = 0;
};

template<typename T>
class MathNode
{
public:
    virtual T evaluate() = 0; // T& ?
};

//////////////////////// Control Nodes ////////////////////////

class IfNode : public ControlNode
{
public:
    IfNode(LogicNode* pExpression, ControlNode* pTrueNode, ControlNode* pFalseNode) : m_pIfTrue(pTrueNode), m_pIfFalse(pFalseNode), m_pExpression(pExpression) {}
       
    void activate() { if(m_pExpression->evaluate()) m_pIfTrue->activate(); else m_pIfFalse->activate(); }

    ControlNode* m_pIfTrue;
    ControlNode* m_pIfFalse;
    LogicNode* m_pExpression;
};

class ActionNode : public ControlNode
{
public:
    Action* m_pAction;
    void activate() { m_pAction->act(); }
};

class TestNode : public ControlNode
{
public:
    TestNode(String testString) : m_sTestString(testString) {}
    void activate() { std::cout << m_sTestString << std::endl; }
    String m_sTestString;
};

//////////////////////// Logic Nodes ////////////////////////

class AndNode : public LogicNode
{
public:
    AndNode(LogicNode* logicNode1, LogicNode* logicNode2) : m_pLogicNode1(logicNode1), m_pLogicNode2(logicNode2) {}
    bool evaluate() { return m_pLogicNode1->evaluate() && m_pLogicNode2->evaluate(); }
private:
    LogicNode* m_pLogicNode1;
    LogicNode* m_pLogicNode2;
};

class OrNode : public LogicNode
{
public:
    OrNode(LogicNode* logicNode1, LogicNode* logicNode2) : m_pLogicNode1(logicNode1), m_pLogicNode2(logicNode2) {}
    bool evaluate() { return m_pLogicNode1->evaluate() || m_pLogicNode2->evaluate(); }
private:
    LogicNode* m_pLogicNode1;
    LogicNode* m_pLogicNode2;
};

template<typename T>
class GreaterThanNode : public LogicNode
{
public:
    GreaterThanNode(MathNode<T>* mathNode1, MathNode<T>* mathNode2) : m_pMathNode1(mathNode1), m_pMathNode2(mathNode2) {}
    bool evaluate() { return (m_pMathNode1->evaluate() > m_pMathNode2->evaluate()); }
private:
    MathNode<T>* m_pMathNode1;
    MathNode<T>* m_pMathNode2;
};

template<typename T>
class EqualityNode : public LogicNode
{
public:
    EqualityNode(MathNode<T>* mathNode1, MathNode<T>* mathNode2) : m_pMathNode1(mathNode1), m_pMathNode2(mathNode2) {}
    bool evaluate() { return (m_pMathNode1->evaluate() == m_pMathNode2->evaluate()); }
private:
    MathNode<T>* m_pMathNode1;
    MathNode<T>* m_pMathNode2;
};

//////////////////////// Math Nodes ////////////////////////

template<typename T>
class AdditionNode : public MathNode<T>
{
public:
    AdditionNode(MathNode<T>* mathNode1, MathNode<T>* mathNode2) : m_pMathNode1(mathNode1), m_pMathNode2(mathNode2) {}
    T evaluate() { return m_pMathNode1->evaluate() + m_pMathNode2->evaluate(); }
private:
    MathNode<T>* m_pMathNode1;
    MathNode<T>* m_pMathNode2;
};

template<typename T>
class MultiplicationNode : public MathNode<T>
{
public:
    MultiplicationNode(MathNode<T>* mathNode1, MathNode<T>* mathNode2) : m_pMathNode1(mathNode1), m_pMathNode2(mathNode2) {}
    T evaluate() { return m_pMathNode1->evaluate() + m_pMathNode2->evaluate(); }
private:
    MathNode<T>* m_pMathNode1;
    MathNode<T>* m_pMathNode2;
};

template<typename T>
class ValueNode : public MathNode<T>
{
public:
    ValueNode(T* pValue) : m_pValue(pValue) {}
    T evaluate() { return *m_pValue; }
private:
    T* m_pValue;
};


#endif
